/* 
   assemble.c
   requirements: https://docs.google.com/a/utexas.edu/document/d/1Zs3BTt8EDvyHripQTq1bJuz5ZG38SaAqflIDEJwzHMs/edit
   assembler for LC3b machine
	
   Yen-Kai Huang
   yh4996

   last edit: Sun Sep 22 23:53:32 CDT 2013

*/

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include "stdint.h"


// #define DEBUG
// #define DEBUG_GETTOK // PASSED!
// #define DEBUG_PB     // PASSED!
// #define DEBUG_PF     // PASSED!
// #define DEBUG_ASS    // PASSED!
// #define DEBUG_UNSIGNED_CONST // PASSED!
// #define DEBUG_SIGNED_CONST   // PASSED!
// #define DEBUG_OFFSET         // PASSED!

#define ENABLE_DEFAULT_OUTPUT_NAME 1

/******************** static functions *******************/
typedef unsigned short ADDRESS;
static void symbol_add(const uint8_t *label, ADDRESS value);
static ADDRESS symbol_get(const uint8_t *label);

int inerror(const char *errmsg);
int error(int errcode);

/* case insensitive compare */
int strincmp(const uint8_t *s1, const uint8_t *s2);

void write(uint16_t data);

/************************ global ************************/
FILE *iFile = NULL, *oFile = NULL;
#define MAXTOKEN 21

int strincmp(const uint8_t *s1, const uint8_t *s2) {
  register int diff;
  for(;; ++s1, ++s2) {
    diff = toupper(*s1) - toupper(*s2);
    if(diff != 0 || !*s1)
      return diff;
  }
}

void write(uint16_t data) {
  fprintf(oFile, "0x%.4X\n", data);
}

ADDRESS objline;

typedef unsigned short Instruction;


/************************* error ************************/
#define ILLEGAL_LABEL    1
#define INVALID_OPCODE   2
#define INVALID_CONSTANT 3
#define OTHER            4

char const *errlist[5] = {
  NULL, /* there is no error code 0 */
  "undefined label",
  "invalid opcode",
  "invalid constant",
  "other error"
};

/* Handle errors thad don't correspond to a error number */
int inerror(const char *errmsg) {
  /* Print the error message to stderr */
  fprintf(stderr, errmsg);

  fclose(iFile);
  fclose(oFile);
  exit(4);
}

int error(int errcode) {
  fprintf(stderr, "Error code %d: %s", errcode, errlist[errcode]);
  
  fclose(iFile);
  fclose(oFile);
  exit(errcode);
}

/******************** Symbol table **********************/
#define MAXSYMBOL 255

struct TabEntry {
  char label[MAXTOKEN+1];
  uint16_t value;
};

static struct TabEntry Table[MAXSYMBOL];
static int symbcount = 0;

static void symbol_add(const uint8_t *label, ADDRESS value) {
  int i;

  for(i = 0; i < symbcount; ++i) {
    if(strcmp((const char *) Table[i].label, (const char *) label) == 0) inerror("registering duplicate symbol");
  }

  if(symbcount <= MAXSYMBOL) {
    strcpy((char *) Table[symbcount].label, (char *) label);
    Table[symbcount].value = value;
    ++symbcount;
  } else {
    inerror("symbol table overflow");
  }
}

static ADDRESS symbol_get(const uint8_t *label) {
  int i;

  for(i = 0; i < symbcount; ++i) {
    if(strcmp((char *) Table[i].label, (char *) label) == 0) return Table[i].value;
  }
  
  /* symbol not found */
  inerror("symbol not found!");
  return -1;
}

/************************ lexer ****************************/
/* static functions */
char tokbuf[MAXTOKEN];
typedef enum {
  STR, REG, xNUM, dNUM, sNUM, CHAR, NEWLN = '\n',  COMMA = ',', WHITESPACE = ' ', EOFCHAR =EOF, ILLEGAL_CHAR,
} Toktype;

static int istoken(int ch);

#define GETC()     getc(iFile)
#define UNGETC(ch) ungetc(ch, iFile)

Toktype getToken(uint8_t *buf, const char *delim) {
  /* Use getc() and ungetc() to get Token
     First skip any number of delimiter characters
     The first non-delimiter will be switched to provide further action
     " ", "\t": eat the whole " ", "\t" string, return SPACE
     "\n": eat the whole "\n" string, return NEWLN
     ";": eat until "\n", return NEWLN
     ",": return COMMA
     "." | "_" | [a-z0-9]: read the whole string of [a-z0-9] until MAXTOKEN, then analyze it.

     Character String analysis:
     [0-9] - invalid at beginning of token
     # - invalid if alone, only [0-9] can follow.
     R - can be a register name Rn, or a string.
     X - invalid if alone. either hex or "XOR", otherwise invalid. As hex only [0-9A-F] can follow
     [A-Z_.] - string, can only be followed by [A-Z_]

     Postcondition will be one of the following
     +------------+--------------------+
     |return      |BUF[]               |
     +------------+--------------------+
     |WHITESPACE  |--                  |
     +------------+--------------------+
     |NEWLN       |--                  |
     +------------+--------------------+
     |COMMA       |--                  |
     +------------+--------------------+
     |STR         |the string          |
     +------------+--------------------+
     |xNUM        |the string of number|
     |            |with X              |
     +------------+--------------------+
     |dNUM        |the string of       |
     |            |number withouh #    |
     +------------+--------------------+
     |sNUM        |number for SPACE    |
     |            |                    |
     +------------+--------------------+
     |REG         |reg number 0-9 is   |
     |            |stored in buf[0]    |
     +------------+--------------------+
     |CHAR        |one character in    |
     |            |buf[0]              |
     +------------+--------------------+
     |EOFCHAR     |--                  |
     +------------+--------------------+

       
  */

  int ch;
  
  //  Toktype type = NEWLN;
  *buf = '\0';
  
  while((ch = GETC()) != EOF) {
    const char *d;
    
    /* 1. skip any number of delimiter characters */
    for(d = delim; *d != '\0' && ch != *d; ++d);
    if(*d != '\0') /* ch is in delim[] */ continue;
    
    switch(ch) {
    case ' ':
      do { ch = GETC(); } while(ch == ' ');
      UNGETC(ch);
      return WHITESPACE;

    case '\t':
      do { ch = GETC(); } while(ch == '\t');
      UNGETC(ch);
      return WHITESPACE;

    case ';':
      do { ch = GETC(); } while(ch != '\n' && ch != '\r' && ch != EOF);
      UNGETC(ch);
      /* intended fall-thru */
    
    case '\n': case '\r':
      do { ch = GETC(); } while(ch == '\n' || ch == '\r');
      UNGETC(ch);
      return NEWLN;

    case ',':
      return COMMA;

    case '\'':
      if(isprint(ch = GETC())) buf[0] = ch, buf[1] = '\0';
      if((ch = GETC()) != '\'') error(OTHER);
      return CHAR;

    default:
      break;
    }
 
    if(istoken(ch)) {
      int len = 0;
      uint8_t* bufptr = buf;

      /* Read the line of token character */
      do {
	*bufptr++ = ch; ++len;
      } while(istoken(ch = GETC()) && len < MAXTOKEN);
      if(!istoken(ch)) UNGETC(ch); /* EOF etc will be put back */
      else { /* check if the token is too long */
	if(istoken(ch = GETC())) error(OTHER);	   
      }
      *bufptr = '\0';
      
      /* data analysis */
      if(isdigit(buf[0])) {
	/* any non-digit is invalid */
	if(buf[0] == '0') error(OTHER); /* to prevent SPACE 0, kind of overkill*/
	bufptr = &buf[1];
	while(*bufptr != '\0') {
	  if(!isdigit(*bufptr)) error(OTHER);
	}

	return sNUM;
      } 

      if(buf[0] == '#') {
	/* a single # is invalid */
	if(*(bufptr = &buf[1]) == '\0') error(OTHER);
	
	/* a digit or a negative sign is allowed immediately after it */
	if(!isdigit(*bufptr) && *bufptr != '-') error(OTHER);
        if(*bufptr == '-' && *(bufptr+1) == '\0') error(OTHER);
	*(bufptr -1) = *bufptr;
	++bufptr;

	/* any non-digit after that is invalid */
	while(*bufptr != '\0') {
	  if(!isdigit(*bufptr)) error(OTHER);
	  *(bufptr -1) = *bufptr;
	  ++bufptr;
	}
	*(bufptr -1) = '\0';
	
	return dNUM;
      }
      
      if(toupper(buf[0]) == 'X') {
	if(strincmp(buf,(uint8_t *) "XOR") == 0) return STR;
	
	/* Lable can't start with X, so can only be hex number */
	/* a single X is invalid*/
	if(*(bufptr = &buf[1]) == '\0') error(OTHER);
	/* any character outside of [0-9A-F] range is invalid */
	while(*bufptr != '\0') {
	  if(!isxdigit(*bufptr)) error(OTHER);
	  ++bufptr;
	}
	return xNUM;
      }

      if(toupper(buf[0]) == 'R') {
	if(len == 2 && isdigit(buf[1]) && buf[1] < '8') {
	  /* convention: put the register number in buf[0]
	     NOTE: This allows R8 and R9 to be string label */
	  buf[0] = buf[1] - '0';
	  buf[1] = '\0';
	  return REG;
	}
	else return STR;
      }
      
      /* All the other characters and _ and . */
      bufptr = &buf[1];
      while(*bufptr != '\0') {
	if(!isalnum(*bufptr) || *bufptr == '_') error(INVALID_CONSTANT);
	++bufptr;
      }
      return STR;
    }
    
    /* Fall-thru to here means ch is not a token character */
    return ILLEGAL_CHAR;

  }

  /* Fall-thru to here means ch==EOF */
  return EOFCHAR;
}

static int istoken(int ch){
  return ch=='.' || ch=='_' || ch=='#' || ch=='-' || isalnum(ch);
}

void goToLineEnd(void) {
  int ch;

  while((ch = GETC()) != EOF && ch != '\r' && ch != '\n');
  if(ch == EOF) UNGETC(ch);
}

#undef GETC
#undef UNGETC

/************************ parser ***************************/
typedef char ConditionCode;

/*Data used by isopcode()*/
typedef enum {
  /*** Required: Order matches that of OPTable ***/
  ADD, AND, BR, JMP, JSR, JSRR, LDB, LDW, LEA, NOP, NOT, RET,
  RTI, LSHF, RSHFA, RSHFL, STB, STW, TRAP, XOR,

  HALT, GETC, OUT, PUTS, IN,

  ORIG, FILL, END, SPACE,    


  MAXOPCODENUM, /* MAXOPCODENUM is not a legal opcode, just holds the total number of types */

  ILLEGAL, /* If input doesn't match any opcode */

} OPType;

typedef enum {
  ALU_FORM, BR_FORM, JMP_FORM, JSR_FORM, LEA_FORM, NOT_FORM, SL_FORM, SHF_FORM, TRAP_FORM, RTI_FORM, PSEUDO,
} InstructForm;

typedef struct  {
  uint8_t str[10];
  uint8_t bin;
  char numOperand;
  InstructForm insform;
} OPCODE_ENTRY;

OPCODE_ENTRY OPTable[MAXOPCODENUM] = {
  /*** Required: Order matches that of OPType ***/
  {"ADD",   0x01, 3, ALU_FORM},
  {"AND",   0x05, 3, ALU_FORM},
  {"BR",    0x00, 1, BR_FORM}, /* Requires special treatment:
				  use op1 for cc, op2 is address */
  {"JMP",   0x0C, 1, JMP_FORM},
  {"JSR",   0x04, 1, JSR_FORM},
  {"JSRR",  0x04, 1, JSR_FORM},
  {"LDB",   0x02, 3, SL_FORM},
  {"LDW",   0x06, 3, SL_FORM},
  {"LEA",   0x0E, 2, LEA_FORM},
  {"NOP",   0x00, 0, RTI_FORM},
  {"NOT",   0x09, 2, NOT_FORM},
  {"RET",   0x0C, 0, JMP_FORM},
  {"RTI",   0x08, 0, RTI_FORM},
  {"LSHF",  0x0D, 3, SHF_FORM},
  {"RSHFA", 0x0D, 3, SHF_FORM},
  {"RSHFL", 0x0D, 3, SHF_FORM},
  {"STB",   0x03, 3, SL_FORM},
  {"STW",   0x07, 3, SL_FORM},
  {"TRAP",  0x0F, 1, TRAP_FORM},
  {"XOR",   0x09, 3, ALU_FORM},

  /* TRAP routines */
  {"HALT",  0x0F, 0, TRAP_FORM},
  {"GETC",  0x0F, 0, TRAP_FORM},
  {"OUT",   0x0F, 0, TRAP_FORM},
  {"PUTS",  0x0F, 0, TRAP_FORM},
  {"IN",    0x0F, 0, TRAP_FORM},
 
  /* Pseudo-Ops are not real instruction and don't have binary code */
  {".ORIG",   -1, 1, PSEUDO},
  {".FILL",   -1, 1, PSEUDO},
  {".END",    -1, 0, PSEUDO},
  {".SPACE",  -1, 1, PSEUDO},
  
};

static ConditionCode parseCC(const uint8_t *cc);
static uint32_t parseOffset(const uint8_t *s, uint32_t pc, uint32_t maxbit);
static uint32_t parseSignedConst(const uint8_t *s, uint32_t maxbit);
static uint32_t parseUnsignedConst(const uint8_t *s, uint32_t maxbit);
static OPType isopcode(const uint8_t *str);

#define PB_IS_LABELED  1 
#define PB_NOT_LABELED 0
#define PB_EOF         -1
int parsebare(uint8_t *label, uint8_t *instr) {
  /* precondition: cursor is at the beginning of a line.
     precondition: label and instr are buffer of at least MAXLEN
     
     getToken result:
     WHITESPACE - label is skipped and expect an opcode
     NEWLN - back to column 0 and expct a label
     STR - depending on the position may be a label or opcode
     (anythin else) - invalid

     postcondition - return the label in label[], opcode in instr[]
     postcondition - if there exist a label return 1, else 0
	
  */

  Toktype type;
  uint8_t buf[MAXTOKEN];
  char column = 0; /* state variable remembering the current column
		      0 - at line beginning
		      1 - has space or tabs before it
		      2 - found an op. Should exit.
		   */

  int isLabeled = PB_NOT_LABELED;
  *label = '\0';
  *instr = '\0';

  while(column != 2) {
    switch(type = getToken(buf, "")) {
    case WHITESPACE:
#ifdef DEBUG_PB
      printf("space ");
#endif

      column = 1;
      break;
     
    case NEWLN:
#ifdef DEBUG_PB
      printf("carriage ");
#endif

      column = 0;
      break;

    case STR:
#ifdef DEBUG_PB
      printf("string! ");
#endif
      if(column == 0) {
	/* caller responsible to check the validity of label */
	strcpy((char *) label, (char *) buf);
	column = 1;
	isLabeled = 1;
      } else { /* column == 1 */
	/* caller responsible to check the validity of instruction */
	strcpy((char *) instr, (char *) buf);
	column = 2;
      }
      break;

    case EOFCHAR:
      /* the document ended before getting a complete line */
      return PB_EOF;

    default:
      error(INVALID_OPCODE);
    }
  }

  return isLabeled;
}

#ifdef DEBUG_PF
void debugtelltype(const char *debugbuffer, int debugint);
#endif

OPType parsefull(Instruction *ins, ADDRESS *nline) {
  /* Precondition: cursor at the beginning of a file
     nline stores the current line number
	
     First call parsebare() to obtain the label and instruction (as a string),
     call goToLineEnd()
     ignore the label, parse the instruction using isopcode(),
     determine how many operands to fetch,
     fetch those operands,
     parse the instruction into binary 16bit value
		
	   
     Postcondition: the parsed hex code is stored in ins
     Postcondition: its opcode type is returned
  */
  uint8_t label[MAXTOKEN];  /* to be ignored */
  uint8_t insbuf[MAXTOKEN]; /* what matters */
  OPType optype;
  Toktype OperandType[3];
  uint8_t Operands[3][MAXTOKEN];
 
  if(parsebare(label, insbuf) == EOF) {
#ifdef DEBUG_PF
    puts("The asembly code ends without .end");
#endif
    error(OTHER);  /* The assembly code ends without .END */
  }
  optype = isopcode(insbuf);


#ifdef DEBUG_PF
  printf("%s: ", OPTable[optype].str);
#endif
	
  {	/* Fetch operands
	   BR needs special treatment
	*/
    Toktype right;
		
    if(optype != BR) {
      int i = 0;
      int total =  OPTable[optype].numOperand;

#ifdef DEBUG_PF
      printf("%d operands: ", OPTable[optype].numOperand);
#endif
	   
      if(total > 0) {
	/* the first operand and the opcode is separated with whitespace only */
	OperandType[i] = getToken(Operands[i], " \t");
	++i;
#ifdef DEBUG_PF
	  debugtelltype(Operands[0], OperandType[0]);
#endif
			
	
	while(i < total) {
	  if(getToken(Operands[i], " \t") != COMMA) {
	    error(OTHER); /* operands are separated by one comma */
	  }  
	  if((OperandType[i] = getToken(Operands[i], " \t")) == NEWLN || OperandType[i] == COMMA) {  /* too few operands*/    
	    error(OTHER);
	  };
			 
#ifdef DEBUG_PF
	  debugtelltype(Operands[i], OperandType[i]);
#endif

	  ++i;
	}
      }
	   
    } else { /* BR special case*/
      strcpy((char *) Operands[0], (char *) &insbuf[2]); /* NZP */
      if((OperandType[1] = getToken(Operands[1], " \t")) != STR) error(INVALID_CONSTANT);
    }

    if((right = getToken(label, " \t,")) != NEWLN && right != EOFCHAR) { /* too many operands */
      error(OTHER);
    }

    /* End of parsing */
    //goToLineEnd();
  }
	
  {	/* Now parse the instruction
	   precondition: 
	*/
#define OPCODE_OFFSET 12
#define DR_OFFSET 9
#define SR_OFFSET 6
#define BaseR_OFFSET 6
		
    int ch = OPTable[optype].bin;
		
    *ins = 0;
    if((signed char) ch > 0) { *ins |= (ch << OPCODE_OFFSET); } /* opcode have negative bin number */
		

    /* operand list */
    switch(OPTable[optype].insform) {
		
#define ALU_STEER 0x20
    case ALU_FORM:
      /* ALU DR, SR1, SR2   or
	 ALU DR, SR1, imm5  or*/
      if(OperandType[0] != REG || OperandType[1] != REG) error(OTHER);
      *ins |= Operands[0][0] << DR_OFFSET;
      *ins |= Operands[1][0] << SR_OFFSET;
	   
      switch(OperandType[2]) {
      case xNUM: case dNUM:
	*ins |= ALU_STEER;
	*ins |= parseSignedConst(Operands[2], 5);
	break;
      case REG:
	*ins |= Operands[2][0];
	break;
      default:
	error(OTHER);
	break;
      }
      break;
#undef ALU_STEER
		
#define BR_CC_OFFSET 9
    case BR_FORM:
      /* BRcc pcoffset9 */
      /* BR *instructions special case
	 Operands[1] stores the NZP string (may be empty)
	 Operands[2] is the address/label
      */
      *ins |= (parseCC(Operands[0]) << BR_CC_OFFSET);
	   
      switch(OperandType[1]) {
      case xNUM: case dNUM:
	*ins |= parseSignedConst(Operands[1], 9);
	break;
      case STR:
	*ins |= parseOffset(Operands[1], *nline, 9);
	break;
      default:
	error(OTHER);
	break;
      }
      break;
#undef BR_CC_OFFSET
		
    case JMP_FORM:
      /* JMP BaseR */
      switch(optype) {
      case RET:
	*ins = 0xC1C0;
	break;
      case JMP:
	if(OperandType[0] == REG) *ins |= Operands[0][0] << BaseR_OFFSET;
	else error(OTHER);
	break;
      default:
	break;
      }
      break;
	   
    case JSR_FORM:
      /* JSR PCoffset11 or
	 JSRR BaseR */	
      switch(OperandType[0]){
      case STR:
	*ins |= parseOffset(Operands[0], *nline, 11);
      case xNUM: case dNUM:
	*ins |= parseSignedConst(Operands[0], 11);
	break;
      case REG:
	*ins |= Operands[0][0] << BaseR_OFFSET;
	break;
      default:
	error(OTHER);
	break;
      }
      break;
	   
    case LEA_FORM:
      /* LEA DR, PCoffset9 */
      if(OperandType[0] == REG) *ins |= Operands[0][0] << DR_OFFSET;
      else error(OTHER);
		  
      switch(OperandType[1]){
      case STR:
	*ins |= parseOffset(Operands[1], *nline, 9);
      case xNUM: case dNUM:
	*ins |= parseSignedConst(Operands[1], 11);
	break;
      default:
	error(OTHER);
	break;
      }
      break;
	   
    case NOT_FORM:
      /* NOT DR, SR */
      if(OperandType[0] == REG && OperandType[1] == REG) {
	*ins |= (Operands[0][0] << DR_OFFSET) | (Operands[1][0] << BaseR_OFFSET);
      } else error(OTHER);

      *ins |= 0x3F;
      break;

    case SL_FORM:
      /* SL DR, BaseR, boffset6 */
      if(OperandType[0] == REG && OperandType[1] == REG) {
	*ins |= (Operands[0][0] << DR_OFFSET) | (Operands[1][0] << BaseR_OFFSET);
      } else error(OTHER);
		  
      switch(OperandType[2]){
      case xNUM: case dNUM:
	*ins |= parseSignedConst(Operands[2], 6);
	break;
      default:
	error(OTHER);
	break;
      }
      break;
	    
#define LSHF_STEER   0x00
#define RSHFA_STEER  0x10
#define RSHFL_STEER  0x30
    case SHF_FORM:
      /* SHF DR, SR, amount4 */
      if(OperandType[0] == REG && OperandType[1] == REG) {
	*ins |= (Operands[0][0] << DR_OFFSET) | (Operands[1][0] << BaseR_OFFSET);
      } else error(OTHER);
		  
      switch(OperandType[2]){
      case xNUM: case dNUM:
	*ins |= parseUnsignedConst(Operands[2], 4);
	break;
      default:
	error(OTHER);
	break;
      }
		  
      switch(optype) {
      case LSHF:
	*ins |= LSHF_STEER;
      case RSHFA:
	*ins |= RSHFA_STEER;
      case RSHFL:
	*ins |= RSHFL_STEER;
      default:
	break;
      }
      break;
#undef LSHF_STEER
#undef RSHFA_STEER
#undef RSHFL_STEER
	   
    case TRAP_FORM:
      /* TRAP trapvect8 */
      /* trapvect8 is inferred from opcode name */
      switch(optype) {
      case TRAP: /* plain trap */
	if(OperandType[0] == xNUM) *ins |= parseUnsignedConst(Operands[0], 8);
	else error(OTHER);
	break;
		   
      case HALT:
	*ins |= 0x25;
	break;
			
      case GETC:
	*ins |= 0x20;
	break;
			
      case OUT:
	*ins |= 0x21;
	break;
			
      case PUTS:
	*ins |= 0x22;
	break;
			
      case IN:
	*ins |= 0x23;
	break;
			
      default:
	break;
      }
      break;
	   
    case RTI_FORM:
      /* no operand */
      break;
	   
    case PSEUDO:
      switch(optype) {
      case ORIG:
	if(OperandType[0] == xNUM) *ins = parseUnsignedConst(Operands[0], 16);
	else error(OTHER);

	/* alignment */
	if(*ins % 2 != 0) error(INVALID_CONSTANT);
	break;

		 
      case FILL:
	switch(OperandType[0]) {
	case xNUM: case dNUM:
	  *ins = parseUnsignedConst(Operands[0], 16);
	  break;

	case CHAR:
	  *ins = Operands[0][0];
	  break;

	default:
	  error(OTHER);
	  break;
	}
	break;
		 
      case SPACE:
	/* requires special treatment */
	/* will directly increase nline*/
	/* the number argument does not need # sign*/
	//if(OperandType[0] != sNUM) error(OTHER);
	//*nline += parseUnsignedConst(Operands[0], 6);

	/* 6 is arbitrary. In fact anything within 16 can theretically work */
	break;

      case END:
	break;

      default:
	break;
      }
      break;
	   
    default:
      break;
    }

    /*  END OF PARSING */
  }

  return optype;
}


static ConditionCode parseCC(const uint8_t *cc) {
  /* precondition: cc contains a valid condition code specifier.

     translate the string of NZP in cc[] into hex NZP
     cc may be empty, in this case parseCC should return NZP.
  */
  ConditionCode result = 0;
  ConditionCode temp;
  enum {
    CC_N = 4, CC_Z = 2, CC_P = 1,
    CC_NZP = 7,
  };

  while(*cc != '\0') {
    switch(toupper(*cc)) {
    case 'N':
      temp = CC_N;
      break;
    case 'Z':
      temp = CC_Z;
      break;
    case 'P':
      temp = CC_P;
      break;
    default:
      error(INVALID_OPCODE);
      break;
    }
    
    result |= temp;

    ++cc;
  }
 
  return (result == 0)? CC_NZP: result;
}


static uint32_t parseUnsignedConst(const uint8_t *s, uint32_t maxbit) {
  uint32_t number;

  if(toupper(*s) == 'X') {
    number = (uint32_t) strtol((const char *) (s+1), NULL, 16);
  } else {
    number = atoi((const char *) s);
  }

#ifdef DEBUG_UNSIGNED_CONST
  printf("number = %d\n", number);
#endif
  
  if(number & (~0 << maxbit)) error(INVALID_CONSTANT);
  return number;
}

static uint32_t parseSignedConst(const uint8_t *s, uint32_t maxbit) {
  signed int number;

  if(toupper(*s) == 'X') {
    number = (signed int) strtol((const char *) s+1, NULL, 16);
    if(number & (~0 << maxbit)) error(INVALID_CONSTANT);
  } else {
    number = (signed int) atoi((const char *) s);
    if(number > ~(~0 << (maxbit-1)) || number < ~0 << (maxbit-1)) error(INVALID_CONSTANT);
  }

#ifdef DEBUG_SIGNED_CONST
  printf("number = %d\n", number);
#endif

  return number & ~(~0 << maxbit);
}

static uint32_t parseOffset(const uint8_t *s, uint32_t pc, uint32_t maxbit) {
  /* Precondition: symtab is already built up */

  signed short number = symbol_get(s) - pc;

#ifdef DEBUG_OFFSET
  printf("number = %d\n", number);
#endif

  /* the range of 4 bit is [-1000, 0111] */
  if(number > ~(~0 << (maxbit-1)) || number < ~0 << (maxbit-1)) error(INVALID_CONSTANT);

  return number & ~(~0 << maxbit);

}

static OPType isopcode(const uint8_t *str) {
  int i;
  const uint8_t BRVariants[][5] = {
    "BRN", "BRZ", "BRP", "BRNZ", "BRZP", "BRNP", "BRNZP",
  };

  for(i = 0; i < MAXOPCODENUM; ++i) {
    if(strincmp(str, OPTable[i].str) == 0) return (OPType) i;      
  }

  /* BR variants */
  for(i = 0; i < 8; ++i) {
    if(strincmp(str, BRVariants[i]) == 0) return BR;
  }

  return ILLEGAL;
}

/************************ assemble **************************/

Instruction currins;

void assemble(void) {

  {  /* First pass
	1. Ensure legal .ORIG
	2. parsebare to return ((label), instruction) pair
	build up symbol table from these info
     */
    uint8_t labelbuf[MAXTOKEN];
    uint8_t insbuf[MAXTOKEN];
    int labelflag;
   
#ifdef DEBUG_ASS
    puts("First pass!\n");
#endif

    /* ORIG */
    if(parsefull(&currins, NULL) != ORIG) error(OTHER);
    /* this ignores the corner case where .ORIG pseudo op comes with a label */
    objline = (ADDRESS) currins;  /* starting address must be even */
    write(objline);

    while((labelflag = parsebare(labelbuf, insbuf)) != PB_EOF) {
      if(labelflag == PB_IS_LABELED) {
	symbol_add(labelbuf, objline);
#ifdef DEBUG_ASS
	printf("\nLabel: %s=0x%.4X", labelbuf, objline);
#endif
      }

      objline += 2;
      goToLineEnd();
    }
   
   
  }

  rewind(iFile);
  objline = 0;

  {  /* Second pass 
	Precondition: the symbol table is established

	run parsefull()
     */
#ifdef DEBUG_ASS
    puts("Second pass!\n");
#endif
    /* ORIG */
    parsefull(&currins, NULL);
    objline = (ADDRESS) currins;

    while(parsefull(&currins, &objline) != END) {
      write(currins);

#ifdef DEBUG_ASS
      printf("\n0x%.4X\n", currins);
#endif
      
      objline += 2;
    }

  }
 
}



/************************** main ****************************/

#ifdef DEBUG /* Declaration of debug-use variables */
 uint8_t debugbuffer[MAXTOKEN];
 uint8_t debugbuffer2[MAXTOKEN];
 int debugint;
 unsigned int debuguint, debuguint2;
 uint8_t *debugptr;
 OPType debugtype;
 Instruction debugins;

 void debugtelltype(const char *debugbuffer, int debugint) {
   switch(debugint) {
   case STR:
     printf("STR: %s ", debugbuffer);
     break;
   case REG:
     printf("REG:");
     break;
   case xNUM:
     printf("xNUM: %s ", debugbuffer);
     break;
   case dNUM:
     printf("dNUM: %s ", debugbuffer);
     break;
   case NEWLN:
     puts("NEWLN");
     break;
   case COMMA:
     puts("COMMA");
     break;
   case WHITESPACE:
     puts("WHITESPACE")
;
     break;

   default:
     break;
   }
 }

#endif

int main(int argc, char *argv[]) {
  char *iFileName, *oFileName = NULL;

  /* Process command line arguments */
#if ENABLE_DEFAULT_OUTPUT_NAME
  switch(argc){
  case 3:
    oFileName = argv[2];
    /* intended fall thru */
  case 2:
    iFileName = argv[1];
    break;
  default:
    fprintf(stderr, "Usage: assemble <source.asm> [<output.obj>]\n" \
	    "if the second argument is missing the output file will of the same name as source");
    exit(4);
  }

  if(!oFileName) {
    int len = strlen(iFileName);
    oFileName = strdup(iFileName);
		
    oFileName[len - 3] = 'o';
    oFileName[len - 2] = 'b';
    oFileName[len - 1] = 'j';
  }
#else

  if(argc == 3){
    iFileName = argv[1];
    oFileName = argv[2];
  } else {
    fprintf(stderr, "Usage: assemble <source.asm> <output.obj>\n");
    exit(4);
  } 

#endif

  /* Open files */
  iFile = fopen(iFileName, "r");
  oFile = fopen(oFileName, "w");

  if (!iFile) {
    fprintf(stderr, "File Error: Cannot open file %s\n", argv[1]);
    exit(4);
  }
  if (!oFile) {
    fprintf(stderr, "File Error: Cannot open file %s\n", argv[2]);
    exit(4);
  }

#ifdef DEBUG_PF_go_away
  puts("This is the testing for parsefull():\n");
  objline = 0;
  while((debugtype = parsefull(&debugins, &objline)) != END) {
    printf("\n0x%.4X\n", debugins);
  }
  rewind(iFile);
  puts("\nThe End\n\n");
#endif

#ifdef DEBUG_UNSIGNED_CONST
  debugptr = "x30";
  printf("%d\n", (unsigned int) strtol(debugptr+1, NULL, 16));

  puts("12 in 4bits");
  debugint = parseUnsignedConst("12", 4);
  printf("0x%.4X\n", debugint);

  puts("765 in 12bits");
  debugint = parseUnsignedConst("765", 12);
  printf("0x%.4X\n", debugint);

  puts("x3000 in 16bits");
  debugint = parseUnsignedConst("x3000", 16);
  printf("0x%.4X\n", debugint);

  puts("x30 in 8bits");
  debugint = parseUnsignedConst("x30", 8);
  printf("0x%.4X\n", debugint);

  puts("x30 in 16bits");
  debugint = parseUnsignedConst("x30", 16);
  printf("0x%.4X\n", debugint);

  puts("30 in 4bits");
  debugint = parseUnsignedConst("x13000", 16);
  printf("0x%.4X\n", debugint);

#endif 

#ifdef DEBUG_SIGNED_CONST

  puts("6 in 4bits");
  debugint = parseSignedConst("6", 4);
  printf("0x%.4X\n", debugint);

  puts("-7 in 4bits");
  debugint = parseSignedConst("-7", 4);
  printf("0x%0.4X\n", debugint);

  puts("765 in 12bits");
  debugint = parseSignedConst("765", 12);
  printf("0x%.4X\n", debugint);

  puts("-8 in 4bits");
  debugint = parseSignedConst("-8", 4);
  printf("0x%0.4X\n", debugint);
#endif 

#ifdef DEBUG_OFFSET
  symbol_add("a", 0x3007);
  symbol_add("b", 0x2ff8);
  symbol_add("c", 0x3008);

  printf("0x%.4X\n", parseOffset("a",4, 0x3000));

  printf("0x%.4X\n", parseOffset("b",4, 0x3000));

  //  printf("0x%.4X\n", parseOffset("c",4, 0x3000));

  
  printf("0x%.4X\n", parseOffset("a",9, 0x3000));

  printf("0x%.4X\n", parseOffset("b",9, 0x3000));

  printf("0x%.4X\n", parseOffset("c",9, 0x3000));

#endif

#ifdef DEBUG_GETTOK
  puts("This is the testing for getTokdn():\n");

  while((debugint = getToken(debugbuffer, " \t")) != EOF) {
    switch(debugint) {
    case STR:
      printf("STR: %s\n", debugbuffer);
      break;
    case REG:
      printf("REG: %s\n", debugbuffer);
      break;
    case xNUM:
      printf("xNUM: %s\n", debugbuffer);
      break;
    case dNUM:
      printf("dNUM: %s\n", debugbuffer);
      break;
    case NEWLN:
      puts("NEWLN");
      break;
    case COMMA:
      puts("COMMA");
      break;
    case WHITESPACE:
      puts("WHITESPACE");
      break;

    default:
      break;

    }
    
  }

  puts("\nThe End");

#endif

  // Assemble
  assemble();

  fclose(iFile);
  fclose(oFile);
  return EXIT_SUCCESS;

}
